/*=============================================================================

  FG HAL MODULE

  FILE: fgHal_emul.h
  
  DESCRIPTION

    FG Emulator

  Copyright (c) DaeSung Engineering Inc. All rights reserved.

=============================================================================*/
/*=============================================================================

  EDIT HISTORY

  ---------  -------  -------------------------------------------------------
    when       who       what, where, why
  ---------  -------  -------------------------------------------------------
   07/17/04   nso4th    Initial version

=============================================================================*/

/*=============================================================================
  FEATURE DEFINITION
=============================================================================*/

/*=============================================================================
  HEADER INCLUSION
=============================================================================*/
#include <OBase.h>
#include <mxType.h>
#include <mxBase/fgDev.h>
#include <mxBase/imp.h>
#include <devSP/dev_mem.h>
#include "fgHal_emul.h"
#include <stdio.h>
#include <string.h>

/*=============================================================================
  CONSTANT DEFINITIONS
=============================================================================*/
#define FGEMUL_CAM_FILE_LEN				(128)

#define FGEMUL_GRABCONT_SIG				(0x100)
#define FGEMUL_GRAB_ASYNC_SIG			(0x200)
#define FGEMUL_GRABCONT_STOP_SIG		(0x800)

#define FGEMUL_WAIT_TIME				(500)

#define FGEMUL_SYNCWAIT_TIME			(1000)

/*=============================================================================
  STRUCTURES
=============================================================================*/
typedef struct
{
	boolean		init;
	dword		dev_id;
	dword		err_code;
	
	dword		grab_mode;
	
	char			szCam[FGEMUL_CAM_FILE_LEN];
	dword		chan_num;
	dword		cam_w;
	dword		cam_h;
	dword		cam_pitch;

	byte			*pData;
	dword		memSize;
	int32		img_cnt;

	mcxt		ss; // simple sync.object
	
	boolean		async_lock;
	boolean		sync_lock;
	mcxt		pObj;
	ObjClient		pCli;
} fgemul_s;

/*=============================================================================
  LOCALIZED FUCNTIONS
=============================================================================*/
static FgHal_s	*gHal = NULL;

// HAL Interface
static dword fgemul_Execute(mcxt pObj, dword dwCmd, dword wParam, dword lParam);
static dword fgemul_SetParam(mcxt pObj, dword dwType, dword wParam, dword lParam);
static dword fgemul_GetParam(mcxt pObj, dword dwType, dword wParam, dword lParam);

// Hardware Dependent
static mcxt fgemul_alloc(mcxt pObj, dword dev_id);
static boolean fgemul_free(fgemul_s *dev);
static boolean fgemul_init(fgemul_s *dev);
static boolean fgemul_close(fgemul_s *dev);

static void fgemul_err_reset(fgemul_s *dev);
static void fgemul_err_set(fgemul_s *dev, dword err_code);

static boolean fgemul_grab_sync(fgemul_s *dev, boolean bCont, byte *pBuf);
static boolean fgemul_grab_async(fgemul_s *dev, boolean bCont);
static boolean fgemul_grab_stop(fgemul_s *dev);
static void emul_grab(fgemul_s *dev);

// Task function
dword __stdcall fgemul_task(void *pCxt);
void fgemul_evt_cb(fgemul_s *dev, dword dwEvent, dword wParam, dword lParam);

/*=============================================================================
  ENUMERATORS
=============================================================================*/

/*=============================================================================
  EXTERNALIZED FUNCION DEFINITION
=============================================================================*/
boolean FgHal_Emul_GetVersion(char *pszVer, int32 strLen)
{
	static char	szTmp[64];

	if(!pszVer)
	{
		return	FALSE;
	}
#ifdef _ODEBUG_
	sprintf(szTmp, "fg_emulD.%s (%s)", "V03.001", __DATE__);
#else
	sprintf(szTmp, "fg_emul.%s (%s)",  "V03.001", __DATE__);
#endif
	if(strLen > (int32)strlen(szTmp))
	{
		strcpy(pszVer, szTmp);
		return	TRUE;
	}

	return	FALSE;
}

boolean FgHal_Emul_Register(void)
{
	FgHal_s		*pHal = gHal;

	if(pHal == NULL)
	{
		gHal = memCalloc(sizeof(*gHal));
		pHal = gHal;
	}

	pHal->dwDevID = FGDEV_EMULATOR;
	pHal->pfnTaskCb = fgemul_task;
	pHal->SetParamF = fgemul_SetParam;
	pHal->GetParamF = fgemul_GetParam;
	pHal->ExecuteF = fgemul_Execute;

	return	FgDev_HalRegister(pHal);
}

boolean FgHal_Emul_Deregister(void)
{
	FgHal_s		*pHal = gHal;

	if(pHal)
	{
		FgDev_HalDeregister(pHal);
		memFree(pHal);
		pHal = NULL;
		gHal = NULL;
	}
	
	return	TRUE;
}

/*=============================================================================
  HAL INTERFACING FUNCTIONS
=============================================================================*/
dword fgemul_SetParam(mcxt pObj, dword dwType, dword wParam, dword lParam)
{
	fgemul_s	*dev = (fgemul_s*)pObj;

	switch(dwType)
	{
	case FGDEV_ISDEVINIT_P:
		return	FALSE;
		
	case FGDEV_CAM_INFO_P:
	{
		char	*pszCam = (char*)wParam;

		//if(dev->init)
		//	return	FALSE;

		if(strlen(pszCam) < (FGEMUL_CAM_FILE_LEN-1))
		{
			strcpy(dev->szCam, pszCam);
			dev->img_cnt = 0;
			emul_grab(dev);
			dev->img_cnt = 0;
			return	TRUE;
		}
		return	FALSE;
	}
	case FGDEV_WIDTH_P:
	{
		if(dev->init)
			return	FALSE;
		dev->cam_w = wParam;
		return	TRUE;
	}
	case FGDEV_HEIGHT_P:
	{
		if(dev->init)
			return	FALSE;
		dev->cam_h = wParam;
		return	TRUE;
	}
	case FGDEV_CHANNUM_P:
	{
		if(dev->init)
			return	FALSE;
		dev->chan_num = wParam;
		return	TRUE;
	}
	case FGDEV_PITCH_P:
	case FGDEV_GRAB_P:
	case FGDEV_GRAB_CONT_P:
		return	TRUE;
	
	case FGDEV_GRABMODE_P:  //User cannot control.
		return	FALSE;

	// Extera Parameters
	case FGDEV_HAL_BASE_P:
		return	FALSE;
		
	case FGDEV_EMUL_IMGCOUNT_P:
		dev->img_cnt = (int32)wParam;
		return	TRUE;
		
	default:
		break;
	}

	return	FALSE;
}

dword fgemul_GetParam(mcxt pObj, dword dwType, dword wParam, dword lParam)
{
	fgemul_s	*dev = (fgemul_s*)pObj;

	switch(dwType)
	{
	case FGDEV_ISDEVINIT_P:
		return	(dword)(dev->init);	

	case FGDEV_CAM_INFO_P:
		return	(dword)(dev->szCam);

	case FGDEV_WIDTH_P:
		return	(dword)(dev->cam_w);
	
	case FGDEV_HEIGHT_P:
		return	(dword)(dev->cam_h);

	case FGDEV_PITCH_P:
		return	(dword)(dev->cam_pitch);

	case FGDEV_CHANNUM_P:
		return	(dword)(dev->chan_num);

	case FGDEV_GRAB_CONT_P:
	case FGDEV_GRAB_P:	
		if(dev->grab_mode == FGDEV_NONE_GRAB)
			return	(boolean)FALSE;

		return	(boolean)TRUE;

	case FGDEV_GRABMODE_P:
		return	dev->grab_mode;

		// Extera Parameters
	case FGDEV_HAL_BASE_P:
		return	FALSE;

	case FGDEV_EMUL_IMGCOUNT_P:
		return	(dword)dev->img_cnt;
		
	default:
		break;
	}
	return	FALSE;
}

dword fgemul_Execute(mcxt pObj, dword dwCmd, dword wParam, dword lParam)
{
	switch(dwCmd)
	{
	case FGDEV_ALLOC_C:
		return	(dword)fgemul_alloc((mcxt)wParam, (dword)lParam);

	case FGDEV_FREE_C:
		return	(dword)fgemul_free((fgemul_s*)pObj);

	case FGDEV_INIT_C:
		return	(dword)fgemul_init((fgemul_s*)pObj);

	case FGDEV_CLOSE_C:
		return	(dword)fgemul_close((fgemul_s*)pObj);

	case FGDEV_LASTERROR_C:
	{
		fgemul_s	*dev = (fgemul_s*)pObj;
		
		return	dev->err_code;
	}
	case FGDEV_GRAB_SYNC_C:
		return	(dword)fgemul_grab_sync((fgemul_s*)pObj, (boolean)wParam, (byte*)lParam);

	case FGDEV_GRAB_ASYNC_C:
		return	(dword)fgemul_grab_async((fgemul_s*)pObj, (boolean)wParam);

	case FGDEV_GRAB_STOP_C:
		return	(dword)fgemul_grab_stop((fgemul_s*)pObj);

	case FGDEV_HAL_BASE_C:
		break;
	default:
		break;
	}

	return	(dword)FALSE;
}
/*=============================================================================
  HARDWARE INTERFACING FUNCTIONS
=============================================================================*/
mcxt fgemul_alloc(mcxt pObj, dword dev_id)
{
	fgemul_s		*dev;

	dev = memCalloc(sizeof(*dev));
	dev->pObj = pObj;
	dev->dev_id = dev_id;
	dev->pCli = FgDev_HalGetClient(pObj);

	fgemul_err_reset(dev);
	
	return	dev;
}

boolean fgemul_free(fgemul_s *dev)
{
	if(dev)
	{
		fgemul_close(dev);
		memFree(dev);
	}

	return	FALSE;
}

boolean fgemul_init(fgemul_s *dev)
{
	if(dev->init)
		return	FALSE;

	fgemul_err_reset(dev);
	
	dev->img_cnt = 0;
	emul_grab(dev);
	dev->img_cnt = 0;
	
	dev->grab_mode = FGDEV_NONE_GRAB;

	dev->ss = Sync_Alloc();
	
	dev->init = TRUE;
	
	return	TRUE;
}

boolean fgemul_close(fgemul_s *dev)
{
	if(!dev->init)
		return	FALSE;

	fgemul_grab_stop(dev);

	if(dev->pData)
	{
		memFree(dev->pData);
		dev->pData = NULL;

		dev->memSize = 0;
	}

	fgemul_err_reset(dev);

	if(dev->ss)
	{
		Sync_Set(dev->ss, TRUE);
		Sync_Free(dev->ss);
		dev->ss = NULL;
	}
	dev->init = FALSE;

	return	TRUE;
}

void fgemul_err_reset(fgemul_s *dev)
{
	dev->err_code = FGDEV_ERROR_BASE_E;
}

void fgemul_err_set(fgemul_s *dev, dword err_code)
{
	dev->err_code = err_code;
	fgemul_evt_cb(dev, dev->err_code, 0, 0);
}


boolean fgemul_grab_sync(fgemul_s *dev, boolean bCont, byte *pBuf)
{
	if(dev->init == FALSE)
	{
		fgemul_err_set(dev, FGDEV_HW_NOTINIT_ERR_E);
		
		return	FALSE;
	}
	if(dev->grab_mode != FGDEV_NONE_GRAB)
	{
		fgemul_err_set(dev, FGDEV_HW_PENDING_ERR_E);
		return	FALSE;
	}

	if(bCont)
	{
		dev->grab_mode = FGDEV_SYNC_CONT_GRAB;
		Task_PutSignal(FgDev_HalGetTCB(dev->pObj), FGEMUL_GRABCONT_SIG);
	}
	else
	{	
		if(pBuf == NULL)
		{
			fgemul_err_set(dev, FGDEV_HW_INVALID_PARAM_ERR_E);
			return	FALSE;
		}

		dev->grab_mode = FGDEV_SYNC_GRAB;
		emul_grab(dev);
		memCopy(pBuf, dev->pData, dev->memSize);
		dev->grab_mode = FGDEV_NONE_GRAB;
	}

	return	TRUE;
}

boolean fgemul_grab_async(fgemul_s *dev, boolean bCont)
{
	if(dev->init == FALSE)
	{
		fgemul_err_set(dev, FGDEV_HW_NOTINIT_ERR_E);
		
		return	FALSE;
	}
	if(dev->grab_mode != FGDEV_NONE_GRAB)
	{
		fgemul_err_set(dev, FGDEV_HW_PENDING_ERR_E);
		return	FALSE;
	}

	if(bCont)
	{
		dev->grab_mode = FGDEV_ASYNC_CONT_GRAB;
		Task_PutSignal(FgDev_HalGetTCB(dev->pObj), FGEMUL_GRABCONT_SIG);
	}
	else
	{	
		dev->grab_mode = FGDEV_ASYNC_GRAB;
		Task_PutSignal(FgDev_HalGetTCB(dev->pObj), FGEMUL_GRAB_ASYNC_SIG);
	}

	return	TRUE;
}

boolean fgemul_grab_stop(fgemul_s *dev)
{
	switch(dev->grab_mode)
	{
	case FGDEV_SYNC_GRAB:
	{
		dev->grab_mode = FGDEV_NONE_GRAB;
		fgemul_evt_cb(dev, FGDEV_GRABSTOP_E, 0, 0);
		break;
	}
	case FGDEV_ASYNC_GRAB:
	case FGDEV_SYNC_CONT_GRAB:
	case FGDEV_ASYNC_CONT_GRAB:
	{
		dev->sync_lock = TRUE;
		Sync_Set(dev->ss, FALSE);
		Task_PutSignal(FgDev_HalGetTCB(dev->pObj), FGEMUL_GRABCONT_STOP_SIG);
		return	Sync_Wait(dev->ss, FGEMUL_SYNCWAIT_TIME);
	}
	case FGDEV_NONE_GRAB:
	default:
		return	TRUE;
	}
	
	return	TRUE;
}

void emul_grab(fgemul_s *dev)
{
	static byte	cnt = 0;
	byte			val;
	dword		i, j;
	Img2D		pImg;
	static char	pszTmp[128];

	if(dev->pData)
	{
		memFree(dev->pData);
		dev->pData = NULL;
	}
	
	sprintf(pszTmp, "%s%00004d.bmp", dev->szCam, dev->img_cnt);
	pImg = imread(pszTmp, "bmp");
	
	if(pImg == NULL)
	{
		dev->img_cnt = 0;
		sprintf(pszTmp, "%s%00004d.bmp", dev->szCam, dev->img_cnt);
		pImg = imread(pszTmp, "bmp");
	}
	dev->img_cnt++;

	if(pImg)
	{
		dev->cam_w = pImg->iWidth;
		dev->cam_h = pImg->iHeight;

		switch(pImg->byType)
		{
		case IMG_TYPE_BIT16:
		{
			dev->chan_num = 2;
			break;
		}
		case IMG_TYPE_BIT24_3CHAN:
		{
			dev->chan_num = 3;
			break;
		}
		case IMG_TYPE_BIT1:
		case IMG_TYPE_BIT8:
		default:
		{
			dev->chan_num = 1;
			break;
		}
		}
		dev->memSize = dev->cam_w * dev->cam_h * dev->chan_num;
		dev->pData = memCalloc(dev->memSize);

		memCopy(dev->pData, pImg->pData, dev->memSize);
		IMP_FREE(pImg);
	}
	else
	{
		dev->cam_w = 1600;
		dev->cam_h = 1200;
		dev->chan_num = 1;
		dev->memSize = dev->cam_w * dev->cam_h * dev->chan_num;
		dev->pData = memCalloc(dev->memSize);
		val = (0x01 << cnt);
		for(j=0; j<dev->cam_h; j++)
		{
			for(i=0; i<dev->cam_w; i++)
			{
				*(dev->pData + i + j*dev->cam_w) = val;
			}
		}
		cnt ++;
		if(cnt >= 8)
		{
			cnt = 0;
		}
	}
	Task_Delay(33);
}

dword __stdcall fgemul_task(void *pCxt)
{
	fgemul_s	*dev = (fgemul_s*)FgDev_HalGetDevice(pCxt);
	mcxt		pTask = FgDev_HalGetTCB(pCxt);
	dword		dwSig;

	while(TRUE)
	{
		dwSig = Task_GetSignal(pTask);

		if(TASK_IS_STARTREQ(dwSig))
		{
			Task_StartACK(pTask);
		}
		if(TASK_IS_STOPREQ(dwSig))
		{
			Task_StopACK(pTask);
			break;
		}
		if(TASK_SIGNAL_ENABLED(dwSig, FGEMUL_GRABCONT_SIG))
		{
			if(((dev->grab_mode == FGDEV_SYNC_CONT_GRAB) ||  (dev->grab_mode == FGDEV_ASYNC_CONT_GRAB)) \
				&& (!TASK_SIGNAL_ENABLED(dwSig, FGEMUL_GRABCONT_STOP_SIG)))
			{
				fgemul_evt_cb(dev, FGDEV_GRAB_START_E, 0, 0);
				emul_grab(dev);
				if(dev->sync_lock == FALSE)
				{
					fgemul_evt_cb(dev, FGDEV_IMAGE_E, (dword)(dev->pData), 0);
				}
			}
			else
			{
				TASK_CLEAR_SIGNAL(pTask, FGEMUL_GRABCONT_SIG);
			}
		}
		if(TASK_SIGNAL_ENABLED(dwSig, FGEMUL_GRAB_ASYNC_SIG))
		{
			TASK_CLEAR_SIGNAL(pTask, FGEMUL_GRAB_ASYNC_SIG);
			if((dev->grab_mode == FGDEV_ASYNC_GRAB) \
				&& (!TASK_SIGNAL_ENABLED(dwSig, FGEMUL_GRABCONT_STOP_SIG)))
			{
				fgemul_evt_cb(dev, FGDEV_GRAB_START_E, 0, 0);
				emul_grab(dev);
				if(dev->sync_lock == FALSE)
				{
					fgemul_evt_cb(dev, FGDEV_IMAGE_E, (dword)(dev->pData), 0);
				}
				dev->grab_mode = FGDEV_NONE_GRAB;
			}
			else
			{
				TASK_CLEAR_SIGNAL(pTask, FGEMUL_GRAB_ASYNC_SIG);
			}
		}
		if(TASK_SIGNAL_ENABLED(dwSig, FGEMUL_GRABCONT_STOP_SIG))
		{
			TASK_CLEAR_SIGNAL(pTask, FGEMUL_GRABCONT_STOP_SIG);
			TASK_CLEAR_SIGNAL(pTask, FGEMUL_GRAB_ASYNC_SIG);
			TASK_CLEAR_SIGNAL(pTask, FGEMUL_GRABCONT_SIG);

			Sync_Set(dev->ss, TRUE);
			dev->sync_lock = FALSE;
			dev->grab_mode = FGDEV_NONE_GRAB;

			fgemul_evt_cb(dev, FGDEV_GRABSTOP_E, 0, 0);
		}
	}

	return	(1);
}

void fgemul_evt_cb(fgemul_s *dev, dword dwEvent, dword wParam, dword lParam)
{
	if(dev->pCli && dev->pCli->EventCbf)
	{
		dev->pCli->EventCbf(dwEvent, wParam, lParam, dev->pCli->pUserCxt);
	}
}
/*--- END OF fgHal_emul.C ---*/
